#include "alice.h"


int memoryTest() {
  int byteCounter = 0; // initialize a counter
  byte *byteArray; // create a pointer to a byte array
  // More on pointers here: http://en.wikipedia.org/wiki/Pointer#C_pointers

  // use the malloc function to repeatedly attempt allocating a certain number of bytes to memory
  // More on malloc here: http://en.wikipedia.org/wiki/Malloc
  while ( (byteArray = (byte*) malloc (byteCounter * sizeof(byte))) != NULL ) {
    byteCounter++; // if allocation was successful, then up the count for the next try
    free(byteArray); // free memory after allocating it
  }
  
  free(byteArray); // also free memory after the function finishes
  return byteCounter; // send back the highest number of bytes successfully allocated
}




/* Astronomical coordinates funtions */

float HHMMSStoDegress(int h,int m,float s)
{
	int sig=1;
	if (h<0) {
		sig=-1;
	} 
	float d=abs(h)+(float)m/60.+(float)s/3600.;
	d=d*sig;
   	return d;
}

float HHMMSStoRadians(int h,int m,float s)
{
     	float r=HHMMSStoDegress(h, m, s)*M_PI/180.;
	return r;
}


void toHorizontal(float Ra,float Dec,float latitude,float LST,float &azimuth,float &altitude)
{

   float HA=LST-Ra; 

   float SinAlt = sin(Dec)*sin(latitude) + cos(Dec)*cos(HA)*cos(latitude);
   float CosAlt = sqrt(1-SinAlt*SinAlt);

   float SinAz  = (-cos(Dec)*sin(HA))/CosAlt;
   float CosAz  = (sin(Dec)*cos(latitude) - cos(Dec)*cos(HA)*sin(latitude))/CosAlt;


   float Az     = atan2(SinAz, CosAz);
   float Alt    = asin(SinAlt);


   if (Az<0) {
	   Az  = 2.*M_PI+Az;
   }
   azimuth=Az; 
   altitude=Alt;		
}

void toEcuatorial(float azimuth,float altitude,float latitude,float LST,float &Ra,float &Dec)
{

   float    SinDec = sin(altitude)*sin(latitude) + cos(altitude)*cos(azimuth)*cos(latitude);
   float    CosDec = sqrt(1 - SinDec*SinDec);

   float    SinHA  = (-cos(altitude)*sin(azimuth))/CosDec;
   float    CosHA  = (sin(altitude)*cos(latitude) - cos(altitude)*cos(azimuth)*sin(latitude))/CosDec;
   float    HA     = atan2(SinHA, CosHA);

   Ra     = LST - HA;
   Dec    = asin(SinDec);

}

float LocalSideralTime(time_t unixtime,float longitude)
{
//	long offset=unixtime-946728000; // seconds from 2000 January 1, 12h UT, UnixTime 946728000
	long offset=unixtime-1293883200; // seconds from 1/1/2011 12:00:00 == unix time 1293883200
	float D = float(offset)/3600.;          
	D=D/24.;				 //days from 2011 January 1, 12h UT
//	float GMST = 18.697374558 + 24.06570982441908*D; //Original formula
	float GMST = 18.7194490728 + 24.06570982441908*D; //modified formula for 2011 12:00

	return fmod(GMST+longitude/15.0,24);
}

float COSangularDistance(float ra1,float dec1,float ra2,float dec2) 
{
//    return cos(M_PI/2-dec1)*cos(M_PI/2-dec2)+sin(M_PI/2-dec1)*sin(M_PI/2-dec2)*cos(ra1-ra2);
    return sin(dec1)*sin(dec2)+cos(dec1)*cos(dec2)*cos(ra2-ra1);
}

float angularDistance(float ra1,float dec1,float ra2,float dec2) 
{
    return acos(COSangularDistance(ra1,dec1,ra2,dec2) );
}


/* ALICE Main Class*/
alice::alice()
{
	Xsize=128;
	Ysize=128;
	X0=64;
	Y0=64;
	size=25;
	Gsize=18;
	imageN=-1;
}


void alice::DB_write_byte(unsigned long address, byte data)
{

}

byte alice::DB_read_byte(unsigned long address)
{
	byte b=readByte(address);
	return b;
}




void alice::showRaDec()
{
	int c=getRGB(63,20,20);
	//text(0,120,"R/D\n",c);
  	showHHMMSS(0,120,(float)(ra*180/(15*M_PI)),c);  
  	showHHMMSS(70,120,(float)(dec*180/M_PI),c); 
}

void alice::showAzAlt()
{
	int c=getRGB(60,10,60);
  	showHHMMSS(0,110,(float)(az*180/M_PI),c);  
  	showHHMMSS(70,110,(float)(alt*180/M_PI),c); 
}

void alice::showLST()
{
	int c=getRGB(60,10,60);	
	text(0,10,"LST:\n",c);
	showHHMMSS(25,10,LST,c);
}		

void alice::showHorizontal()
{
  	showRaDec();
   	toHorizontal(ra,dec,lat*M_PI/180, LST*15*M_PI/180,az,alt);
	showAzAlt(); 
}

void alice::showEcuatorial()
{
	showAzAlt(); 
   	toEcuatorial(az,alt,lat*M_PI/180, LST*15*M_PI/180,ra,dec);
  	showRaDec();
}

void alice::showTemp()
{
   int a=analogRead(PIN_TEMP_SENSOR);	
   float T=5*100*(float)a/1024.; 	
   text(90,10,(float)(T-273.15-1.6),getRGB(60,10,20));  

}

void alice::showCrossPoint()
{
	static int old_x=-1,old_y=0;
	if (old_x!=-1) {
        	showImage(min(max(old_x-5,0),118),min(max(old_y-5,0),118),10,10,0x00FFFF);	
	}
	float _ra=fmod(ra*180/M_PI,Gsize);
	float _dec=fmod((dec*180/M_PI)+90,Gsize);
        float  x=128-_ra*128/Gsize;
        float  y=128-_dec*128/Gsize;
	imageToSD(min(max(x-5,0),118),min(max(y-5,0),118),10,10,0x00FFFF);
	circle(x,y,4,getRGB(63,60,10));
	putPixel(x,y,getRGB(63,60,10));
	old_x=x;
	old_y=y;
}

void alice::showMark(float r,float d, int t)
{
	float _ra=fmod(r*180/M_PI,Gsize);
	float _dec=fmod((d*180/M_PI)+90,Gsize);
        float  x=128-_ra*128/Gsize;
        float  y=128-_dec*128/Gsize;
	switch (t)
	{
		case 0:					   //Crosspoint
			circle(x,y,4,getRGB(63,60,10));
			putPixel(x,y,getRGB(63,60,10));

		case 1:                                    //STARS
			circle(x,y,3,getRGB(30,30,60));
			break;
		case 2:					   //MESSIER									
			rectangle(x-4,y-4,x+4,y+4,getRGB(65,0,0));
			break;
		case 3:					   //NGC						
			circle(x,y,2,getRGB(60,60,60));
			break;

		case 4:					   //NEAREST	
			rectangle(x-4,y-4,x+4,y+4,getRGB(63,0,60));
			//circle(x,y,2,getRGB(65,65,0));
			break;

		case 5:					   //NEAREST	ALTERNATE
			rectangle(x-4,y-4,x+4,y+4,getRGB(65,65,0));
			//circle(x,y,2,getRGB(30,0,60));
			break;

	}
}

void alice::showLabel(float r,float d, char* t)
{
	float _ra=fmod(r*180/M_PI,Gsize);
	float _dec=fmod((d*180/M_PI)+90,Gsize);
        float  x=128-_ra*128/Gsize;
        float  y=128-_dec*128/Gsize;
	text(x,y,t,getRGB(0,60,0));
}


void alice::showDistanceTo(float ra0,float dec0)
{
	int c=getRGB(63,20,50);
	float d=angularDistance(ra,dec,ra0,dec0); 
  	showHHMMSS(0,100,(float)(d*180/(M_PI)),c);  
}

int  alice::tileN()  
{
	int  t;
	int raN=(int)((ra*180/M_PI)/Gsize);
	int decN=(int)((90+dec*180/M_PI)/Gsize);
	t=decN*(360/Gsize)+raN;
	return 	t;
}

void alice::showMemory()
{
  int result = memoryTest();
  text(100,100,result,getRGB(0,30,50)); 
}

void alice::showScreen(int n)
{
LST=LocalSideralTime(now(),lon);

switch (n)
	{	
	case 1:
		showEcuatorial();
		showCrossPoint();
		showTemp();
		showClock();	
 		showLST();
		if (imageN!=tileN()) {
			imageN=tileN();
			//clear();	
			Image(imageN,DSS2atlas);
			showObjects();		
			text(80,100,tileN(),getRGB(0,60,0));
			opaqueText();
		}

		break;
	case 2:
		opaqueText();
		penSolid();
		rectangle(0,30,128,90,getRGB(0,0,0));
		showEcuatorial();
		showTemp();
		showClock();	
  	//	D3drawCompass();
 		showLST();
		showMemory();
		break;
	case 3:
		showNGC();
		break;

	case 4:
		showHorizontal();
		if ( imageN!=tileN()) {
			imageN=tileN();
			Image(imageN,DSS2atlas);
			showObjects();	
		}
		showCrossPoint();
		showTemp();
		showClock();	
 		showLST();
		break;
		
	}
}

void alice::showNGC()
{
	ngc ngc0;
	char s[3];
	DBopen(0);
	for (int i=1;i<DBcount()+1;i++)
	{	
		int color=getRGB(0,50,50);
		DBreadRec(i, DB_REC ngc0);
		switch (ngc0.ngc_ic_messier)
		{
			case 0:
				s[0]='N';
				s[1]='G';		
				s[2]='C';				
				break;
			case 1:
				s[0]=' ';
				s[1]=' ';		
				s[2]='I';				
				break;

		}
         	sprintf(buffer,"%s%u                  \n",s,ngc0.ngc_n);
                text(0,10,buffer,color);
		text(0,20,ngc0.ra,color);
		text(0,30,ngc0.dec,color);
		text(0,40,"                      ",color);
		text(0,40,ngc0.name,color);
		text(0,50,ngc0.type,color);
		text(0,60,(int)ngc0.image,color);
		text(0,70,(int)ngc0.tile,color);
	}
}

void alice::Image(unsigned long image,int z)
{
	showImage(0,0,128,128,offset[z]/512+image*64);
}

void alice::showMR()
{
	master m;
	int color=getRGB(0,50,50);
	DBopen(0);
	clear();
	for (int i=0;i<DBcount();i++)
	{
		DBreadRec(i+1, DB_REC m);
	     	text(0,10*i,m.name,color);
	     	text(90,10*i,(int)m.numRecords,color);
	}
}
		
void  alice::SDinit()
{
	master m;
	DBopen(0);
	for (int i=0;i<DBcount();i++)
	{
		DBreadRec(i+1, DB_REC m);
		offset[i]=m.offset;
        }

}

void  alice::listObjects()
{
	int color=getRGB(0,50,50);
	objects obj;
	DBopen(offset[OBJECTS_IN_TILE]);
	DBreadRec(imageN+1, DB_REC obj);
     	text(0,20,imageN,color);
     	text(50,20,obj.tile,color);
     	text(0,30,obj.stars_from,color);
     	text(50,30,obj.stars_to,color);
     	text(0,40,obj.ngc_from,color);
     	text(50,40,obj.ngc_to,color);
     	text(0,50,obj.messier_from,color);
     	text(50,50,obj.messier_to,color);
}

void  alice::showObjects()
{
	int color=getRGB(0,50,50);
	objects obj;
	star star0;
	ngc ngc0;
	DBopen(offset[OBJECTS_IN_TILE]);
	DBreadRec(imageN+1, DB_REC obj);
	DBopen(offset[NAMED_STARS]);
     	for (int i=obj.stars_from;i<obj.stars_to;i++) {
		DBreadRec(i+1, DB_REC star0);
		showMark(star0.ra,star0.dec,1);
		showLabel(star0.ra,star0.dec,star0.bayer);
		
	}	
	DBopen(offset[MESSIER]);
     	for (int i=obj.messier_from;i<obj.messier_to;i++) {
		DBreadRec(i+1, DB_REC ngc0);
		showMark(ngc0.ra,ngc0.dec,2);
		showLabel(ngc0.ra,ngc0.dec,ngc0.name);
	}	

	DBopen(offset[NGC]);
     	for (int i=obj.ngc_from;i<obj.ngc_to;i++) {
		DBreadRec(i+1, DB_REC ngc0);
		showMark(ngc0.ra,ngc0.dec,3);
		showLabel(ngc0.ra,ngc0.dec,ngc0.name);
	}	

}

void alice::showNearest(int t)
{
	static byte state=4;
	static int old_nearest;
	int nearest=0;
	float dis=1000000,Ora,Odec;
	objects obj;

	DBopen(offset[OBJECTS_IN_TILE]);
	DBreadRec(imageN+1, DB_REC obj);
	switch (t)
	{
	case NAMED_STARS:
		star star0;
		DBopen(offset[NAMED_STARS]);
     		for (int i=obj.stars_from;i<obj.stars_to;i++) {
			DBreadRec(i+1, DB_REC star0);	
			if (dis>=angularDistance(ra,dec,star0.ra,star0.dec)) {
				Ora=star0.ra; Odec=star0.dec; 
				dis=angularDistance(ra,dec,Ora,Odec);
				nearest=i+1;
			}
		}	

		break;

	case NGC:
		ngc ngc0;
		DBopen(offset[NGC]);
     		for (int i=obj.ngc_from;i<obj.ngc_to;i++) {
			DBreadRec(i+1, DB_REC ngc0);	
			if (dis>=angularDistance(ra,dec,ngc0.ra,ngc0.dec)) {
				Ora=ngc0.ra; Odec=ngc0.dec; 
				dis=angularDistance(ra,dec,Ora,Odec);
				nearest=i+1;
			}
		}
		break;

	case MESSIER:
		ngc ngc1;
		DBopen(offset[MESSIER]);
     		for (int i=obj.messier_from;i<obj.messier_to;i++) {
			DBreadRec(i+1, DB_REC ngc1);	
			if (dis>=angularDistance(ra,dec,ngc1.ra,ngc1.dec)) {
				Ora=ngc1.ra; Odec=ngc1.dec; 
				dis=angularDistance(ra,dec,Ora,Odec);
				nearest=i+1;
			}
		}
		break;
	}
	if  (nearest !=0) {
		showMark(Ora,Odec,state);
		if (state==4) {
			state=5;
		} else  {
			state=4;
		}
       		showDistanceTo(Ora,Odec);
		if (old_nearest!=nearest & dis>0.01){
			Image(imageN,DSS2atlas);
			old_nearest=nearest;	
		}
		if (old_nearest!=0 & dis<0.01) {
				showImage(4010);
				old_nearest=0;
		} 		
	}

}


void  alice::showMessierData(int M)
{
	int color=getRGB(0,50,50);
	ngc m;
	DBopen(offset[MESSIER]);
	DBreadRec(M, DB_REC m);
     	text(0,10,m.name,color);
     	text(0,20,m.type,color);
     	text(0,30,m.ngc_n,color);
}



